﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="GenericListTypeConverter.cs" company="">
//   
// </copyright>
// <summary>
//   The generic list type converter.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace Skymate.ComponentModel
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Globalization;
    using System.Linq;

    /// <summary>
    /// The generic list type converter.
    /// </summary>
    /// <typeparam name="T">
    /// </typeparam>
    public class GenericListTypeConverter<T> : TypeConverter
    {
        /// <summary>
        ///     The _type converter.
        /// </summary>
        protected readonly TypeConverter _typeConverter;

        /// <summary>
        ///     Initializes a new instance of the <see cref="GenericListTypeConverter{T}" /> class.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// </exception>
        public GenericListTypeConverter()
        {
            this._typeConverter = TypeDescriptor.GetConverter(typeof(T));
            if (this._typeConverter == null)
            {
                throw new InvalidOperationException("No type converter exists for type " + typeof(T).FullName);
            }
        }

        /// <summary>
        /// The get string array.
        /// </summary>
        /// <param name="input">
        /// The input.
        /// </param>
        /// <returns>
        /// The <see cref="string[]"/>.
        /// </returns>
        protected virtual string[] GetStringArray(string input)
        {
            if (!string.IsNullOrEmpty(input))
            {
                var result = input.Split(',');
                Array.ForEach(result, s => s.Trim());
                return result;
            }

            return new string[0];
        }

        /// <summary>
        /// The can convert from.
        /// </summary>
        /// <param name="context">
        /// The context.
        /// </param>
        /// <param name="sourceType">
        /// The source type.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(string))
            {
                var items = this.GetStringArray(sourceType.ToString());
                return items.Count() > 0;
            }

            return base.CanConvertFrom(context, sourceType);
        }

        /// <summary>
        /// The convert from.
        /// </summary>
        /// <param name="context">
        /// The context.
        /// </param>
        /// <param name="culture">
        /// The culture.
        /// </param>
        /// <param name="value">
        /// The value.
        /// </param>
        /// <returns>
        /// The <see cref="object"/>.
        /// </returns>
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            if (value is string)
            {
                var items = this.GetStringArray((string)value);
                var result = new List<T>();
                Array.ForEach(
                    items, 
                    s =>
                        {
                            var item = this._typeConverter.ConvertFromInvariantString(s);
                            if (item != null)
                            {
                                result.Add((T)item);
                            }
                        });

                return result;
            }

            return base.ConvertFrom(context, culture, value);
        }

        /// <summary>
        /// The convert to.
        /// </summary>
        /// <param name="context">
        /// The context.
        /// </param>
        /// <param name="culture">
        /// The culture.
        /// </param>
        /// <param name="value">
        /// The value.
        /// </param>
        /// <param name="destinationType">
        /// The destination type.
        /// </param>
        /// <returns>
        /// The <see cref="object"/>.
        /// </returns>
        public override object ConvertTo(
            ITypeDescriptorContext context, 
            CultureInfo culture, 
            object value, 
            Type destinationType)
        {
            if (destinationType == typeof(string))
            {
                var result = string.Empty;
                if (((IList<T>)value) != null)
                {
                    // we don't use string.Join() because it doesn't support invariant culture
                    for (var i = 0; i < ((IList<T>)value).Count; i++)
                    {
                        var str1 = Convert.ToString(((IList<T>)value)[i], CultureInfo.InvariantCulture);
                        result += str1;

                        // don't add comma after the last element
                        if (i != ((IList<T>)value).Count - 1)
                        {
                            result += ",";
                        }
                    }
                }

                return result;
            }

            return base.ConvertTo(context, culture, value, destinationType);
        }
    }
}